/*
 * Simulation.cpp
 *
 *  Created on: Jan 23, 2011
 *      Author: Helpful Learn Group
 */


#include "Simulation.h"


#include <iostream>
#include <cmath>
using namespace std;

const float PI = 3.141592654;

namespace GSP280
{

	//=========================
	//CLASS METHODS
	//=========================
	float DEG2RAD(float a) { return float(a * (PI/180) ); }
	float RAD2DEG(float a) { return float(a * (180/PI) ); }

	//=========================
	//PRIVATE METHODS
	//=========================

	//M001:
	const float Simulation::calc_AngularMomentum() const
	{
		//AngularMomentum(L) = mass * radius^2 * angularMomentum(omega)
		return (float( mass * (radius * radius) *omega) );
	}

	//M002:
	const float Simulation::calc_TangentialVelocity() const
	{
		//Vt = radius * angular Velocity(omega)
		return ( float(radius * omega) );
	}

	//M003:
	const float Simulation::calc_Vx() const
	{
		return (float ( calc_TangentialVelocity() * cosf(angle)));
	}

	//M004:
	const float Simulation::calc_Vy() const
	{
		return (float ( calc_TangentialVelocity() * sinf(angle)));
	}

	//M005:
	const float Simulation::calc_TotalTime() const
	{
		//Find time = ?, using Vf = Vi + a.t,
		//NOTE: Since finding time in Vy ONLY gets time 1/2 way thru projectile
		//SO multiply time * 2
		if(float( (2 * (0 - Vy)) / (-9.8) ) < 0)
		{
			return (
					-1 * (float( (2 * (0 - Vy)) / (-9.8) ))
					);
		}
		else
		{
			return (
					float( (2 * (0 - Vy)) / (-9.8) )
					);
		}
	}

	//M006:
	const float Simulation::calc_ProjectileHeight ( )
	{
		//Find time = ?, using Vf = Vi + a.t,
		float time = (0 - Vy) / (-9.8);

		//Find projectile Height, using deltaY = Viy.t + 0.5 (a)(t^2)
		return (float (
				(Vy * time) + ( 0.5 * (-9.8) * (time * time) )
				) );
	}

	//M007:
	const float Simulation::calc_ProjectileRange()
	{
		//Range formula: deltaX = Vx * time
		return ( float(Vx * totalTime) );
	}

	//M008: use this method ONLY if M007 output >= (targetObj.X )
	const Vector2 Simulation::calc_FlyingObj_NewLocation()
	{
		if ( projectileRange > targetObj.getLocation_X() )
		{
			//Using TotalTime variable to find (x,y) of flying obj

			//using deltaX = Initial Horizontal Velocity(Vx) * time
			//Use the targetObj's x-Location as launch Obj's x-location
			float time = (targetObj.getLocation_X() / Vx);
			
			//using the time it took the flying Obj to reach location of the target obj
			//Find projectile Height, using deltaY = Viy * t + 0.5 (a)(t^2) @ t= time
			float tempY = ( (Vy * time) + ( 0.5 * (-9.8) * (time * time) ) );

			//return location of launch Obj closest to target Obj
			return Vector2(targetObj.getLocation_X(), tempY);
		}
		else { return Vector2(projectileRange, 0); }
	}

	//M009:
	// calculate the new angular momentum (ogema)
	// to adjust the trajectory of the launch object
	void Simulation::calc_NewAngularVelocity()
	{
		float temp;

		Vector2 l_obj, t_obj;
		l_obj = launchObj.getLocation();
		t_obj = targetObj.getLocation();

		// check if the launched object has fell under the Target object 
		//if(launchObj.getLocation_X() < targetObj.getLocation_X())
		if(l_obj.getX() < t_obj.getX())
		{
			// temp = (Target_X - Launch_X) / 2
			temp = ((t_obj.getX() - l_obj.getX()) / 2);

			// temp = (temp * omega) / Launch_X = Extra omega needed to travel the needed distance
			temp = (temp * omega)/l_obj.getX();

			// omega = omega + extra omega
			omega += temp;
		}
		// check if the launched object has flown over the Target object 
		//else if(launchObj.getLocation_X() > targetObj.getLocation_X())
		else if(l_obj.getX() > t_obj.getX())
		{
			// temp = (Target_X - Launch_X) / 2
			temp = ((l_obj.getX() - t_obj.getX()) / 2);

			// temp = (temp * omega) / Launch_X = Extra omega needed to travel the needed distance
			temp = (temp * omega)/t_obj.getX();

			// omega = omega - extra omega
			omega -= temp;
		}
		// If X-Axis of Launched Object and Target Object are same but Y-Axis is under
		//else if((launchObj.getLocation_X() == targetObj.getLocation_X()) && (launchObj.getLocation_Y() < targetObj.getLocation_Y()))
		else if(l_obj.getX() == t_obj.getX() && l_obj.getY() < t_obj.getY())
		{
			// temp = (Target_X - Launch_X) / 2
			temp = ((t_obj.getY() - l_obj.getY()) / 2);

			// temp = (temp * omega) / Launch_X = Extra omega needed to travel the needed distance
			temp = (temp * omega)/l_obj.getY();

			// omega = omega + extra omega
			omega += temp;	
		}
		// If X-Axis of Launched Object and Target Object are same but Y-Axis is over
		//else if((launchObj.getLocation_X() == targetObj.getLocation_X()) && (launchObj.getLocation_Y() > targetObj.getLocation_Y()))
		else if((l_obj.getX() == t_obj.getX()) && (l_obj.getY() > t_obj.getY()))
		{
			// temp = (Target_X - Launch_X) / 2
			temp = ((l_obj.getY() - t_obj.getY()) / 2);

			// temp = (temp * omega) / Launch_X = Extra omega needed to travel the needed distance
			temp = (temp * omega)/t_obj.getY();

			// omega = omega - extra omega
			omega -= temp;
		}
	}

	//M010: Gets called repeatedly until a successful collision has been detected
	void Simulation::calc_Simulation()
	{
		// calculate the new radius
		radius = sqrt(angMomentum/(mass * omega));
		
		// calculate new tangential velocity
		Vt = calc_TangentialVelocity();	

		// calculate new X and Y velocities
		Vx = calc_Vx();
		Vy = calc_Vy();

		// calculate the new time of flight
		totalTime = calc_TotalTime();

		// calculate the projectiles new height
		projectileHeight = calc_ProjectileHeight();

		// calculate the projectiles new range/distance
		projectileRange = calc_ProjectileRange();

		// set the launch objects object
		launchObj.setLocation(calc_FlyingObj_NewLocation());
	
		// NEXT STAGE WILL CHECK FOR COLLISION HERE
	}
	
	void Simulation::SimLoop()
	{
		//bool hasCollided = false;
		cout << counter <<"\t";
		cout << launchObj.intersects(targetObj);

		do{
			// get new angular Velocity
			calc_NewAngularVelocity();

			// complete necessary calculations
			calc_Simulation();

			//hasCollided = launchObj.intersects(targetObj);
			
			counter++;
			cout << counter <<"\t";

			display();
			//system("cls");

		}while(!launchObj.intersects(targetObj));//!hasCollided);

		display();
	}


	//=========================
	//PUBLIC METHODS
	//=========================

	void Simulation::Start ( const int & mass, const int & radius,
			const float & omega, const float & angle, const Vector2 &vecLaunch,
			const Vector2 &vecTarget )
	{
		counter = 1;

		this->mass = mass;
		this->radius = radius;
		this->omega = omega;

		initOmega = omega;
		initRadius = radius;

		// angle is converted to radians because the cosf() function takes
		// in radians within its parameter.
		this->angle = DEG2RAD(angle);

		launchObj.setLocation(vecLaunch); //set the location of launch obj
		launchObj.setRadius( (double)radius );

		targetObj.setLocation(vecTarget); //set the location of target obj
		targetObj.setRadius( (double)radius );

		angMomentum = calc_AngularMomentum();
		Vt = calc_TangentialVelocity();				// Launching Velocity at a specific angle

		Vx = calc_Vx();								// Flying objects initial X-Axis velocity
		Vy = calc_Vy();								// Flying objects initial Y-Axis velocity

		totalTime = calc_TotalTime();				// Total time the Object was in flight
		projectileHeight = calc_ProjectileHeight();	// Max projectile height(vertex)
		projectileRange = calc_ProjectileRange();	// Range/Distance of projectile

		launchObj.setLocation(calc_FlyingObj_NewLocation()); // Objects "closest point" to target

		// runs the game loop that tracks if the launched object has collided with the target object
		SimLoop();

		//bool hasCollided = launchObj.intersects(targetObj); // Testing whether object has collided with target
	}

	void GSP280::Simulation::display ( )
	{
		cout << "\n\n============================================================================\n\n\n";

		cout << "*************** INITIAL INPUT FROM USER ***************" << endl;
		cout << "\n\tInitial Radius: " << initRadius << endl;
		cout << "\tInitial Angular Velocity(Omega): " << initOmega << endl << endl;
		cout << "*************** INITIAL INPUT FROM USER ***************"<< endl << endl;

		cout << "Simulation loop ran " << counter << " times\n\n";

		cout << "Angle in radians: " << angle << endl;
		cout << "Angle in degrees: " << RAD2DEG(angle) << endl << endl;


		cout << "Angle of momentum: " << angMomentum << endl;
		cout << "Radius of Catapult: " << radius << endl << endl;

		cout << "Angular Velocity (omega): " << omega << endl << endl;
		cout << "Tangential Velocity: " << Vt << endl << endl;

		cout << "Initial Velocity (X-Axis): " << Vx << endl;
		cout << "Initial Velocity (Y-Axis): " << Vy << endl << endl;

		cout << "Total Time of flight: " << totalTime << endl << endl;

		cout << "Max Projectile Height: " << projectileHeight << endl;
		cout << "Range/Distance of Projectile: " << projectileRange << endl << endl;

		cout << "Launch Object Location: (" << launchObj.getLocation_X() << " , " << launchObj.getLocation_Y() << ")" << endl;
		cout << "Target Object Location: (" << targetObj.getLocation_X() << " , " << targetObj.getLocation_Y() << ")" << endl << endl;

		cout << "============================================================================\n\n";
	}

}//namespace











	/*
	Simulation::Simulation ( )
	{
		// TODO Auto-generated constructor stub

	}
	
	Simulation::~Simulation ( )
	{
		// TODO Auto-generated destructor stub
	}

}*/
